package br.unifor.mia.sd.proxy;

import java.io.IOException;
import java.net.UnknownHostException;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;

import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

import br.unifor.mia.sd.cache.CacheConfiguration;
import br.unifor.mia.sd.cache.CacheManager;

public class WSDispatcher implements Dispatcher {

	Logger log = Logger.getLogger("br.unifor.mia.sd.proxy");

	/**
	 * @throws IOException
	 * @throws UnknownHostException
	 * @see br.unifor.mia.sd.proxy.Dispatcher#dispatch(br.unifor.mia.sd.proxy.HttpMessage)
	 */
	public HttpMessage dispatch(HttpMessage message)
			throws UnknownHostException, IOException {

		Document document = message.getSoapMessage();
		String key = prepareKey(document);

		HttpMessage response = null;
		byte[] fromCache = null;
		if (key != null) {
			fromCache = CacheManager.retrieve(key);
		}

		if (fromCache != null) {
			try {
				response = new HttpMessage(fromCache);
				if (log.isLoggable(Level.INFO)) {
					log.log(Level.INFO, "Cache hit for key: " + key);
				}
			} catch (InvalidCreationException e) {
				e.printStackTrace();
			}
		} else {
			HttpDispatcher disp = new HttpDispatcher();
			response = disp.dispatch(message);
			if (key != null) {
				if (log.isLoggable(Level.INFO)) {
					log.log(Level.INFO, "Cache miss for key: " + key);
				}
				CacheManager.put(key, response.getMessage());
			} else {
				if (log.isLoggable(Level.INFO)) {
					log.log(Level.INFO, "Not cacheable: " + key);
				}
			}
		}

		return response;
	}

	private String prepareKey(Document document) {
		StringBuilder key = new StringBuilder("__");
		Node body = document.getElementsByTagNameNS("*",
				ProxyConstants.SOAP_BODY).item(0);
		boolean isCacheable = false;

		NodeList childNodes = body.getChildNodes();
		Node operation = null;
		CacheConfiguration conf = CacheConfiguration.getInstance();

		for (int i = 0; i < childNodes.getLength(); i++) {
			operation = childNodes.item(i);
			if (operation.getNodeType() == Node.ELEMENT_NODE) {
				String namespaceURI = operation.getNamespaceURI();
				String nodeName = operation.getLocalName();
				if (conf.isCacheable(namespaceURI, nodeName)) {
					key.append(operation.getLocalName()).append("__(");
					isCacheable = true;
					break;
				}
			}

		}

		if (isCacheable) {
			// Getting Parameters
			// NodeList parameters = operation.getChildNodes();
			Set<String> cacheableParameters = conf.getCacheableParameters(
					operation.getNamespaceURI(), operation.getLocalName());
			for (String param : cacheableParameters) {
				Node paramNode = document.getElementsByTagNameNS(
						operation.getNamespaceURI(), param).item(0);
				key.append(paramNode.getLocalName()).append("=");
				key.append(paramNode.getFirstChild().getNodeValue())
						.append("|");
			}
			key.append(")__");
			return key.toString();
		} else {
			return null;
		}

	}

}
